/***************************************************************************
 *   Copyright (C) 2007 by Gaetano Mendola  *
 *   mendola@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <iostream>
#include <iomanip>
#include <assert.h>

#include "weapon.h"
#include "timer.h"
#include "shaman.h"

//Supports
#include "support/dice.h"

//Events
#include "events/eventdispatcher.h"
#include "events/event.h"
#include "events/tick.h"
#include "events/swing.h"
#include "events/crit.h"
#include "events/windfury.h"

//Buffs
#include "buffs/windfury.h"

Weapon::Weapon(const std::string aName, double aSpeed, double aDPS)
:theName(aName),
 theSpeed(aSpeed*1000, Stats::WEAPON_SPEED), //Internal speed is in millisecond
 theDPS(aDPS),
 theLastSwing(0),
 thePlayer(0)
{
}

Weapon::~Weapon()
{
    Events::EventDispatcher::instance().unsubscribe(this);
}

void Weapon::notify(const Events::Event* anEvent)
{
    Events::Event::TYPE myType = anEvent->type();

    if ( myType == Events::Event::TICK ) {
        const Events::Tick *myEvent = dynamic_cast<const Events::Tick *>(anEvent);

        unsigned long myCurrentTick = myEvent->tick(); //milliseconds

        unsigned long myTimeSinceLastSwing = (myCurrentTick - theLastSwing);

        if ( myTimeSinceLastSwing >= theSpeed ) { //this is a Swing

            swing(theSpeed * theDPS / 1000.0, myCurrentTick);
            unsigned long myLastWF = Shaman::instance().lastWF();

            if ( (myCurrentTick - myLastWF) >= 3000 ) { // milliseconds
                if ( Support::Dice::instance().proc(20.0)  ) { //WF proc is 20%
                    Buffs::WindFury *myBuff = new Buffs::WindFury();
                    thePlayer->addBuff(myBuff);

                    Shaman::instance().lastWF() = myCurrentTick;

                    Events::WindFury* myEvent = new Events::WindFury();
                    Events::EventDispatcher::instance().notify( myEvent );

                    //WF damage buff
                    swing(theSpeed * theDPS / 1000.0 + 34*theSpeed / 1000.0, myCurrentTick);
                    swing(theSpeed * theDPS / 1000.0 + 34*theSpeed / 1000.0, myCurrentTick);

                    thePlayer->removeBuff(myBuff);
                    delete myBuff;

                } 
            } 
        }
    } 
}

void Weapon::equiped(Player *aPlayer)
{
    assert(!thePlayer);
    thePlayer = aPlayer;

    //Register the weapon to TICK events
    Events::EventDispatcher::instance().subscribe(Events::Event::TICK, this);

    //Add to player stats list the attack speed
    thePlayer->addStat(&theSpeed);
}

void Weapon::swing(double aDamage, unsigned long aCurrentTick) 
{
    theLastSwing = aCurrentTick;

    bool aIsACrit = Support::Dice::instance().proc( Shaman::instance().critPercentage() );

    if ( aIsACrit ) {
        aDamage *= 1.5;
    } 

    Events::Swing * myEvent = new Events::Swing(*this, aDamage);
    Events::EventDispatcher::instance().notify(myEvent);

    if ( aIsACrit ) {
        Events::Crit * myEvent = new Events::Crit();
        Events::EventDispatcher::instance().notify(myEvent);
    }
}


